#include "W25Q128.h"
static const char* TAG = "W25Q128";

void W25Q128_Init(W25Q128 *dev)
{
    esp_err_t ret;
    spi_bus_config_t buscfg={
      .mosi_io_num = PIN_NUM_MOSI,
      .miso_io_num = PIN_NUM_MISO,
      .sclk_io_num = PIN_NUM_CLK,
      .quadwp_io_num=-1,
      .quadhd_io_num=-1,
      .max_transfer_sz=MX_TRANS_LEN
     };

   spi_device_interface_config_t W25Q128_BUS_config={
      .clock_speed_hz=W25Q128_CLK_FREQ_MHz*1000*1000,
      .mode = 0,
      .spics_io_num = PIN_NUM_CS,
      .queue_size = 16,
      .pre_cb = NULL,
      .post_cb = NULL,
      .command_bits = 8,
      .address_bits = 24,
    };

    dev->content = (uint8_t *)malloc(4096+1)+1;//extra 1 byte for dummy;
    dev->write_buf = (uint8_t *)malloc(4096);
    memset(dev->write_buf,0x00,4096);
    memset(dev->content,0x00,4096);
    dev->w25q128_handle = &(dev->w25q128);

    ret = spi_bus_initialize(HSPI_HOST,&buscfg,1);
   #ifdef FLASH_DEBUG
    ESP_LOGI(TAG,"SPI init errcode = %d",ret);
   #endif
   ret = spi_bus_add_device(HSPI_HOST,&W25Q128_BUS_config,dev->w25q128_handle);
   #ifdef FLASH_DEBUG
    ESP_LOGI(TAG,"SPI add_device errcode = %d",ret);   
   #endif

}
void W25Q128_SPI_Init(spi_device_handle_t *spi_W25Q128)
{
      esp_err_t ret;
      spi_bus_config_t buscfg={
      .mosi_io_num = PIN_NUM_MOSI,
      .miso_io_num = PIN_NUM_MISO,
      .sclk_io_num = PIN_NUM_CLK,
      .quadwp_io_num=-1,
      .quadhd_io_num=-1,
      .max_transfer_sz=MX_TRANS_LEN
   };

   spi_device_interface_config_t W25Q128_BUS_config={
      .clock_speed_hz=W25Q128_CLK_FREQ_MHz*1000*1000,
      .mode = 0,
      .spics_io_num = PIN_NUM_CS,
      .queue_size = 16,
      .pre_cb = NULL,
      .post_cb = NULL,
      .command_bits = 8,
      .address_bits = 24,
    //   .dummy_bits = 8  //some bugs here
   };

   ret = spi_bus_initialize(HSPI_HOST,&buscfg,1);
   #ifdef FLASH_DEBUG
    ESP_LOGI(TAG,"SPI init errcode = %d",ret);
   #endif
   ret = spi_bus_add_device(HSPI_HOST,&W25Q128_BUS_config,spi_W25Q128);
   #ifdef FLASH_DEBUG
    ESP_LOGI(TAG,"SPI add_device errcode = %d",ret);   
   #endif
}


void W25Q128_Write_Enable(W25Q128 *dev)
{
    esp_err_t ret;
    spi_transaction_ext_t t_ext;
    memset(&t_ext, 0, sizeof(t_ext));

    t_ext.base.cmd = W25X_WriteEnable;
    t_ext.base.length = 0;
    t_ext.base.rxlength = 0;
    t_ext.base.flags = SPI_TRANS_VARIABLE_CMD+SPI_TRANS_VARIABLE_ADDR;//Write Enable Command do not need addr phase and rx phase
    t_ext.base.addr = 0;

    t_ext.command_bits = BYTE;
    t_ext.dummy_bits = 0;
    t_ext.address_bits = 0;

    
    ret = spi_device_transmit(*(dev->w25q128_handle),(spi_transaction_t*)&t_ext);//start
    #ifdef FLASH_DEBUG
        ESP_LOGI(TAG," W25Q128_set_Write_Enable errcode = %d",ret);
    #endif
}

void W25Q128_Read_Status(W25Q128 *dev)
{
    esp_err_t ret;
    spi_transaction_ext_t t_ext;
    memset(&t_ext, 0, sizeof(t_ext));
    

    t_ext.base.cmd = W25X_ReadStatusReg_1;
    t_ext.base.length = BYTE;
    t_ext.base.rxlength = BYTE;
    t_ext.base.flags = SPI_TRANS_VARIABLE_CMD+SPI_TRANS_VARIABLE_ADDR+SPI_TRANS_USE_RXDATA;//Read SR Command do not need addr phase
    
    t_ext.address_bits = 0;
    t_ext.command_bits = BYTE;

    ret = spi_device_transmit(*(dev->w25q128_handle),(spi_transaction_t*)&t_ext);
    // #ifdef FLASH_DEBUG
    //     ESP_LOGI(TAG," W25Q128_read_SR_1 errcode = %d",ret);
    // #endif
    dev->sr.SR1= t_ext.base.rx_data[0];
    #ifdef FLASH_DEBUG
        ESP_LOGI(TAG," W25Q128_SR_1 is 0x%x",dev->sr.SR1);
    #endif

    t_ext.base.cmd = W25X_ReadStatusReg_2;
    ret = spi_device_transmit(*(dev->w25q128_handle),(spi_transaction_t*)&t_ext);
    #ifdef FLASH_DEBUG
        ESP_LOGI(TAG," W25Q128_read_SR_2 errcode = %d",ret);
    #endif
    dev->sr.SR2= t_ext.base.rx_data[0];
    #ifdef FLASH_DEBUG
        ESP_LOGI(TAG," W25Q128_SR_2 is 0x%x",dev->sr.SR2);
    #endif

    t_ext.base.cmd = W25X_ReadStatusReg_3;
    ret = spi_device_transmit(*(dev->w25q128_handle),(spi_transaction_t*)&t_ext);
    #ifdef FLASH_DEBUG
        ESP_LOGI(TAG," W25Q128_read_SR_3 errcode = %d",ret);
    #endif
    dev->sr.SR_3 = t_ext.base.rx_data[0];
    #ifdef FLASH_DEBUG
        ESP_LOGI(TAG," W25Q128_SR_3 is 0x%x",dev->sr.SR_3);
    #endif
}

void W25Q128_Write_Disable(spi_device_handle_t *spi_W25Q128)
{
    esp_err_t ret;
    spi_transaction_ext_t t_ext;
    memset(&t_ext, 0, sizeof(t_ext));

    t_ext.base.cmd = W25X_WriteDisable;
    t_ext.base.length = 0;
    t_ext.base.rxlength = 0;
    t_ext.base.flags = SPI_TRANS_VARIABLE_CMD+SPI_TRANS_VARIABLE_ADDR;//Write Enable Command do not need addr phase and rx phase
    t_ext.base.addr = 0;
    t_ext.dummy_bits = 0;
    t_ext.address_bits = 0;
    t_ext.command_bits = BYTE;
    
    ret = spi_device_transmit(*spi_W25Q128,(spi_transaction_t*)&t_ext);//start
    #ifdef FLASH_DEBUG
        ESP_LOGI(TAG," W25Q128_set_Write_Disable errcode = %d",ret);
    #endif
}


void W25Q128_Read_sector(W25Q128 *dev,uint16_t sector,uint16_t offset,uint16_t length)
{
    esp_err_t ret;
    spi_transaction_ext_t t_ext;
    memset(&t_ext, 0, sizeof(t_ext));//t_ext must be set to all 0 before setup

    ESP_LOGI(TAG,"READ start");

    dev->sector_addr = sector;//record operate pointor to dev_handle
    dev->addr_offset = offset;
    
    t_ext.base.addr = (sector<<12) + offset;
    t_ext.base.cmd = W25X_FastReadData;

    t_ext.base.length = (length+1)*BYTE;//one byte dummy
    t_ext.base.rxlength = (length+1)*BYTE;

    t_ext.base.flags = SPI_TRANS_VARIABLE_CMD+SPI_TRANS_VARIABLE_ADDR ;
    t_ext.address_bits = 3*BYTE;//fast read operate needs 24 bits addr
    t_ext.command_bits = BYTE;//fast read operate needs 8 bits cmd
    t_ext.dummy_bits = BYTE;//fast read operate needs 8 bits dummy some bugs here

    #ifdef FLASH_DEBUG
        ESP_LOGI(TAG," W25Q128_read_addr: 0x%x",(int)t_ext.base.addr);
    #endif

    t_ext.base.rx_buffer = dev->content-1;// one dummy

    ret = spi_device_transmit(*(dev->w25q128_handle),(spi_transaction_t*)&t_ext);//start transaction
    #ifdef FLASH_DEBUG
        ESP_LOGI(TAG," W25Q128_read_errcode: %d",ret);
        // ESP_LOGI(TAG," W25Q128_read_content: 0x%x",dev->content[0]);
    #endif  
}


void W25Q128_Erase_sector(W25Q128 *dev,uint16_t sector)
{
    esp_err_t ret;
    spi_transaction_ext_t t_ext;
    memset(&t_ext, 0, sizeof(t_ext));//t_ext must be set to all 0 before setup
    #ifdef FLASH_DEBUG
        ESP_LOGI(TAG,"ERASE start");
    #endif


    if(!W25Q128_Check_busy(dev))
    {
        W25Q128_Write_Enable(dev);
        dev->sector_addr = sector;//record operate pointor to dev_handle
        t_ext.base.addr = (sector<<12);//left shift 12 bit 
        t_ext.base.cmd = W25X_SectorErase;

        t_ext.base.length = 0;//erase operate do not need read and send
        t_ext.base.rxlength = 0;//do not need to read

        t_ext.base.flags = SPI_TRANS_VARIABLE_CMD+SPI_TRANS_VARIABLE_ADDR;
        t_ext.address_bits = 3*BYTE;//erase operate needs 24 bits addr
        t_ext.command_bits = BYTE;//erase operate needs 8 bits cmd

        #ifdef FLASH_DEBUG
            ESP_LOGI(TAG," W25Q128_erase_addr: 0x%x",(int)t_ext.base.addr);
        #endif
        ret = spi_device_transmit(*(dev->w25q128_handle),(spi_transaction_t*)&t_ext);//send erase cmd
        #ifdef FLASH_DEBUG
            ESP_LOGI(TAG," W25Q128_erase_errcode: %d",ret);
        #endif  

        while(W25Q128_Check_busy(dev))
        {
            ;//wait for write finish
        }
        #ifdef FLASH_DEBUG
            ESP_LOGI(TAG," W25Q128_erase_complete: %d",ret);
        #endif  
    }
    else
    {
        ESP_LOGI(TAG," W25Q128 erase failed,chip is busy");
        return;
    }
}

bool W25Q128_Check_busy(W25Q128 *dev)
{
    bool ret;
    W25Q128_Read_Status(dev);
    if (dev->sr.SR1 & W25Q128_BUSY_bit)
    {
        // #ifdef FLASH_DEBUG
        //     ESP_LOGI(TAG," W25Q128 busy");
    }
       else
    {
        // #ifdef FLASH_DEBUG
        //     ESP_LOGI(TAG," W25Q128 stanby");
        // #endif 
        ret = W25Q128_STANDBY;
    }
    return ret;
}


void W25Q128_PAGE_write(W25Q128 *dev,uint16_t sector,uint16_t page,uint8_t *src)//Write 1 page;
{
    esp_err_t ret;
    spi_transaction_ext_t t_ext;
    memset(&t_ext, 0, sizeof(t_ext));//t_ext must be set to all 0 before setup
    #ifdef FLASH_DEBUG
        ESP_LOGI(TAG,"PAGE_write start");
    #endif

    if(!W25Q128_Check_busy(dev))
    {
        W25Q128_Write_Enable(dev);
        W25Q128_Read_Status(dev);
        dev->sector_addr = sector;//record operate pointor to dev_handle
        dev->addr_offset = page<<8;//1 page contain 256 BYTES
        

        t_ext.base.cmd = W25X_PageProgram;
        t_ext.base.addr = (dev->sector_addr<<12) + dev->addr_offset;
        t_ext.base.length = 256*BYTE;
        t_ext.base.rxlength = 0;//do not need recive
        t_ext.base.tx_buffer = src;
        t_ext.base.flags = SPI_TRANS_VARIABLE_CMD+SPI_TRANS_VARIABLE_ADDR;


        t_ext.address_bits = 3*BYTE;//page write operate needs 24 bits addr
        t_ext.command_bits = BYTE;//page write operate needs 8 bits cmd

        #ifdef FLASH_DEBUG
            ESP_LOGI(TAG,"Write addr : 0x%x",(unsigned int)t_ext.base.addr);
        #endif
        ret = spi_device_transmit(*(dev->w25q128_handle),(spi_transaction_t*)&t_ext);//send erase cmd

        while(W25Q128_Check_busy(dev))
        {
            ;//wait for write finish
        }
        #ifdef FLASH_DEBUG
            ESP_LOGI(TAG," W25Q128_erase_complete: %d",ret);
        #endif          
    }
    else{
    #ifdef FLASH_DEBUG
        ESP_LOGI(TAG," W25Q128 busy");
    #endif 
    }
}

void W25Q128_Read_Unique_ID(W25Q128 *dev)
{
    esp_err_t ret;
    spi_transaction_ext_t t_ext;
    memset(&t_ext, 0, sizeof(t_ext));//t_ext must be set to all 0 before setup
    
    t_ext.base.addr  = 0x000000;//3 BYTES addr as dummy;
    t_ext.base.cmd   = W25X_Unique_ID;
    t_ext.base.length = 3*BYTE;
    t_ext.base.rxlength = 3*BYTE;
    t_ext.base.flags = SPI_TRANS_VARIABLE_CMD + SPI_TRANS_VARIABLE_ADDR;
    t_ext.base.rx_buffer = &(dev->unique_DeviceID);

    t_ext.address_bits = 3*BYTE;
    t_ext.command_bits = BYTE;

    ret = spi_device_transmit(*(dev->w25q128_handle),(spi_transaction_t*)&t_ext);
    #ifdef FLASH_DEBUG
        ESP_LOGI(TAG,"Unique ID : 0x%x",(unsigned int)dev->unique_DeviceID);
    #endif
}

void W25Q128_Read_Mnufact_DeviceID(W25Q128 *dev)
{
    esp_err_t ret;
    spi_transaction_ext_t t_ext;
    memset(&t_ext, 0, sizeof(t_ext));//t_ext must be set to all 0 before setup

    t_ext.base.cmd   = W25X_ManufactDeviceID;
    t_ext.base.length = 2*BYTE;
    t_ext.base.rxlength = 2*BYTE;
    t_ext.base.flags = SPI_TRANS_VARIABLE_CMD + SPI_TRANS_VARIABLE_ADDR;
    t_ext.base.rx_buffer = &(dev->manufacture_ID);
    t_ext.base.addr = 0x000000;

    t_ext.address_bits = 3*BYTE;
    t_ext.command_bits = BYTE;

    ret = spi_device_transmit(*(dev->w25q128_handle),(spi_transaction_t*)&t_ext);
    #ifdef FLASH_DEBUG
        ESP_LOGI(TAG,"Manufacture ID : 0x%x",(unsigned int)dev->manufacture_ID);
    #endif    

}